package demoMod.stsconsole.potions;

import demoMod.stsconsole.ConsoleEntry;
import demoMod.stsconsole.annotations.DataSaver;
import demoMod.stsconsole.core.AbstractCreature;
import demoMod.stsconsole.dungeons.AbstractDungeon;
import demoMod.stsconsole.enums.SubscriberPriority;
import demoMod.stsconsole.helpers.LocalizedStrings;
import demoMod.stsconsole.helpers.RandomSequence;
import demoMod.stsconsole.interfaces.*;
import demoMod.stsconsole.saves.PotionSaver;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@DataSaver(cls = PotionSaver.class)
public abstract class AbstractPotion implements ReloadStringsSubscriber, CustomSavable<Map<String, Object>>, Saleable, ChangePriceSubscriber, PurchaseSubscriber {
    public String id;
    public String name;
    public String description;
    public PotionColor color = PotionColor.SHARED;
    public PotionRarity rarity;
    public UseAction action;
    public Target target;
    public int potential;

    public boolean isPriceInitialized = false;
    private int price;

    public AbstractPotion(String id) {
        this.id = id;
        ConsoleEntry.subscribers.add(this);
        onReloadStrings();
    }

    public abstract String getUpdatedDescription();

    public abstract void use(AbstractCreature target);

    public abstract boolean canUse();

    @Override
    public int getPriority() {
        return SubscriberPriority.POTION.getPriority();
    }

    @Override
    public boolean enabled(Class<? extends ISubscriber> cls) {
        return cls.getSimpleName().equals("ReloadStringsSubscriber") || cls.getSimpleName().equals("ChangePriceSubscriber") || (AbstractDungeon.player != null && AbstractDungeon.player.hasPotion(this));
    }

    @Override
    public void onReloadStrings() {
        List<String> potionStrings = LocalizedStrings.getPotionStrings(id);
        this.name = potionStrings.get(0);
        this.description = potionStrings.get(1);
    }

    @Override
    public Map<String, Object> onSave() {
        Map<String, Object> ret = new HashMap<>();
        ret.put("potential", this.potential);
        return ret;
    }

    @Override
    public void onLoad(Map<String, Object> data) {
        if (data != null) {
            this.potential = (int) data.getOrDefault("potential", 0);
        }
    }

    public AbstractPotion makeCopy() {
        try {
            return this.getClass().newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    public void onRemove() {
        ConsoleEntry.removeLater.add(this);
    }

    @Override
    public int onChangePrice(Saleable product, int price, RandomSequence rng) {
        if (product == this) {
            if (!isPriceInitialized) {
                price *= (rng.nextFloat() - 0.5F) * 0.2F + 1.0F;
                this.isPriceInitialized = true;
            }
            this.price = price;
        }
        return price;
    }

    @Override
    public void calculatePrice(RandomSequence rng) {
        this.price = getBasePrice();
        ConsoleEntry.triggerSubscribers(
                ChangePriceSubscriber.class,
                "onChangePrice",
                new Class[]{Saleable.class, int.class, RandomSequence.class},
                new Object[]{this, this.price, rng},
                (method, params, subscriber) -> {
                    if (params[0] == this) {
                        try {
                            params[1] = method.invoke(subscriber, params);
                        } catch (IllegalAccessException | InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }
                }
        );
    }

    @Override
    public int getBasePrice() {
        int price = 9999;
        switch (this.rarity) {
            case COMMON:
                price = 50;
                break;
            case UNCOMMON:
                price = 80;
                break;
            case RARE:
                price = 100;
                break;
            case SPECIAL:
                price = 999;
                break;
        }
        return price;
    }

    @Override
    public int getPrice() {
        return this.price;
    }

    @Override
    public void purchase() {
        AbstractDungeon.player.obtainPotion(this);
    }

    @Override
    public boolean canPurchase(Saleable product, int price) {
        if (product == this) {
            return AbstractDungeon.player.getPotions().size() < AbstractDungeon.player.maxPotionSlot;
        }
        return true;
    }

    @Override
    public void afterPurchase(Saleable product, int price) {

    }

    public enum PotionColor {
        SHARED, RED, GREEN, BLUE, PURPLE
    }

    public enum PotionRarity {
        COMMON, UNCOMMON, RARE, SPECIAL
    }

    public enum UseAction {
        DRINK, THROW
    }

    public enum Target {
        SELF, ENEMY, ALL_ENEMY
    }
}
